"""
Itinerary manipulation views.
"""

from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404

from track.models import Checkpoint
from track.models import Itinerary
from track.models import Vehicle
from math import sqrt

from re import compile

@login_required
def set(request, vehicle, itinerary):
  """Link 'vehicle' to 'itinerary'"""
  vehicle   = get_object_or_404(Vehicle,   slug=vehicle)
  itinerary = get_object_or_404(Itinerary, slug=itinerary)
  
  vehicle.checkpoint, vehicle.advancement = getAdvancement(itinerary, vehicle.x, vehicle.y)
  
  vehicle.save()
  
  return HttpResponseRedirect('/track/')

@login_required
def save(request, itinerary):
  """Save an itinerary's checkpoints"""
  itinerary = get_object_or_404(Itinerary, slug=itinerary)
  
  currentCheckpoints = list(itinerary.checkpoint_set.all())
  newCheckpoints     = request.POST.getlist('checkpoints')
  
  currentCheckpoints += [None for i in range(len(currentCheckpoints), len(newCheckpoints))]
  newCheckpoints     += [None for i in range(len(newCheckpoints),     len(currentCheckpoints))]
  
  order = 0
  checkpointRegexp = compile(r'^\((-?[0-9\.]+), (-?[0-9\.]+)\)$')
  
  for (checkpoint, newCheckpointString) in zip(currentCheckpoints, newCheckpoints):
    if newCheckpointString == None:
      for vehicle in Vehicle.objects.filter(checkpoint=checkpoint):
        vehicle.checkpoint  = None
        vehicle.advancement = 0.0
        
        vehicle.save()
      
      checkpoint.delete()
    else:
      checkpointMatch = checkpointRegexp.match(newCheckpointString)
    
      newX, newY = checkpointMatch.group(2), checkpointMatch.group(1)
    
      if checkpoint == None:
        itinerary.checkpoint_set.create(x=newX, y=newY, order=order)
      else:
        checkpoint.x = newX
        checkpoint.y = newY
        
        checkpoint.save()
      
      order += 1
  
  return HttpResponse()

def getAdvancement(itinerary, x, y, prevCheckpoint=None):
  """Obtain a point's nearest checkpoint and advancement on an itinerary"""
  checkpoints   = itinerary.checkpoint_set.all()
  nbCheckpoints = len(checkpoints)
  
  if nbCheckpoints < 2:
    return None, 0
  
  bestDist = float("infinity")
  
  for i in range(0, nbCheckpoints - 1):
    chk1 = checkpoints[i]
    chk2 = checkpoints[i + 1]
    
    advancement = _advancement(
      x, y,
      chk1.x, chk1.y,
      chk2.x, chk2.y)
    
    xProj = chk1.x * (1 - advancement) + advancement * chk2.x
    yProj = chk1.y * (1 - advancement) + advancement * chk2.y
    
    dist = (x - xProj) ** 2 + (y - yProj) ** 2
    
    if dist < bestDist:
      bestDist        = dist
      bestCheckpoint, bestAdvancement = chk1, advancement
  
  return bestCheckpoint, bestAdvancement

def _advancement(x, y, chk1x, chk1y, chk2x, chk2y):
  """
  Calculate projection of (x, y) on the segment defined by [(chk1x, chk1y), (chk2x, chk2y)]
  
  Tests for this method are currently doctests in track.tests.testAdvancement
  """
  return min(max(
    0.,
    (((x - chk1x) * (chk2x - chk1x) + (y - chk1y) * (chk2y - chk1y))
      / ((chk2x - chk1x) ** 2 + (chk2y - chk1y) ** 2))),
    1.)
